package dk.peterbn.intervaltrainer;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.widget.Toast;
import dk.peterbn.intervaltrainer.model.Interval;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

/**
 * The main service that runs the timer.
 *
 * @author pebn
 */
public class TimerService extends Service implements TextToSpeech.OnUtteranceCompletedListener{

    public static final String NEW_INTERVAL_NOTICE_UTTERANCE_KEY = "NEW_INTERVAL_NOTICE";
    private static final int NOTIFICATION = R.string.local_service_started;

    //Parameters for the TTS system
    public static final HashMap<String,String> TTS_PARAMS = new HashMap<String, String>() {{
        put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, NEW_INTERVAL_NOTICE_UTTERANCE_KEY);
    }};

    private final Binder BINDER = new ServiceBinder();

    private TextToSpeech tts;
    private Handler handler;
    private Runnable timerJob;

    private NotificationManager notificationManager;
    private AudioManager audioService;
    private IntervalTrainer.UpdateListCallback updateListCallback;

    public void setTts(TextToSpeech tts) {
        this.tts = tts;
        this.tts.setOnUtteranceCompletedListener(this);
    }

    public void startTimer(List<Interval> intervals) {

        final Iterator<Interval> iterator = intervals.iterator();
        if (iterator.hasNext()) {
            playAndScheduleNextInterval(iterator.next(), iterator);
        }
    }

    private void playAndScheduleNextInterval(Interval interval, final Iterator<Interval> iterator) {
        String speakString = getSpeakString(interval);

        audioService.requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
        tts.speak(speakString, TextToSpeech.QUEUE_FLUSH, TTS_PARAMS);

        if (updateListCallback != null) {
            updateListCallback.setActiveInterval(interval);
        }

        if (iterator.hasNext()) {
            timerJob = new TimerJob(iterator);
            handler.postDelayed(timerJob, interval.getDelayMilis());
        }
    }

    private String getSpeakString(Interval interval) {
        Resources res = getResources();
        int minutes = interval.getMinutesPart();
        String minuteString = res.getQuantityString(R.plurals.minute, minutes,minutes);
        int seconds = interval.getSecondsPart();
        String secondsString = res.getQuantityString(R.plurals.second, seconds,seconds);
        return res.getString(R.string.next_interval_announcement,
                interval.getDescription(),
                minuteString,
                secondsString);
    }

    public void stopTimer() {
        handler.removeCallbacks(timerJob);
    }

    public void onUtteranceCompleted(String s) {
        if (NEW_INTERVAL_NOTICE_UTTERANCE_KEY.equals(s)) {
            audioService.abandonAudioFocus(audioFocusChangeListener);
        }
    }

    public void skipForward() {
        if (timerJob != null) {
            //Remove the current job from the queue and run it immediately
            stopTimer();
            timerJob.run();
        }
    }

    public void setUpdateListCallback(IntervalTrainer.UpdateListCallback updateListCallback) {
        this.updateListCallback = updateListCallback;
    }

    public class ServiceBinder extends Binder {
        TimerService getService() {
            return TimerService.this;
        }
    }

    public IBinder onBind(Intent intent) {
        notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        showNotification();
        handler = new Handler();
        audioService = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        return BINDER;
    }

        @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("LocalService", "Received start id " + startId + ": " + intent);
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }


    @Override
    public void onDestroy() {
        // Cancel the persistent notification.
        notificationManager.cancel(NOTIFICATION);
        stopTimer();
        // Tell the user we stopped.
        Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
    }


        /**
     * Show a notification while this service is running.
     */
    private void showNotification() {
        // In this sample, we'll use the same text for the ticker and the expanded notification
        CharSequence text = getText(R.string.local_service_started);

        // Set the icon, scrolling text and timestamp
        Notification notification = new Notification(R.drawable.icon, text,
                System.currentTimeMillis());

        // The PendingIntent to launch our activity if the user selects this notification
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, IntervalTrainer.class), 0);

        // Set the info for the views that show in the notification panel.
        notification.setLatestEventInfo(this, getText(R.string.local_service_label),
                       text, contentIntent);

        // Send the notification.
        notificationManager.notify(NOTIFICATION, notification);
    }

    private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
            public void onAudioFocusChange(int i) {
                //To change body of implemented methods use File | Settings | File Templates.
            }
        };

    private class TimerJob implements Runnable {
        private final Iterator<Interval> iterator;

        public TimerJob(Iterator<Interval> iterator) {
            this.iterator = iterator;
        }

        public void run() {
            if (iterator.hasNext()) {
                playAndScheduleNextInterval(iterator.next(), iterator);
            }
        }
    }
}
